<!--
 * @Author: 梁青春
 * @Date: 2025-10-13 16:46:15
 * @LastEditTime: 2025-11-18 15:02:43
 * @LastEditors: 梁青春
-->
<template>
  <div class="custom-tabs">
    <!-- Tab导航栏 -->
    <div class="tabs-nav" ref="tabsNavRef">
      <div
        v-for="(tab, index) in tabs"
        :key="tab.value"
        :class="['tab-item', { active: modelValue === tab.value }]"
        @click="handleTabClick(tab, index)"
        ref="tabItemRefs"
      >
        <el-badge :hidden="!tab.badge || tab.badge === '0'" :value="tab.badge" :offset="tab.offset">{{
          tab.label
        }}</el-badge>
      </div>
      <!-- 下划线指示器 -->
      <div class="tab-indicator" :style="indicatorStyle" ref="indicatorRef"></div>
    </div>
  </div>
</template>

<script setup>
import { ref, computed, nextTick, onMounted, watch } from 'vue'

// 定义组件属性
const props = defineProps({
  // 当前选中的tab值
  modelValue: {
    type: [String, Number],
    default: ''
  },
  // tab数据数组
  tabs: {
    type: Array,
    default: () => [],
    validator: value => {
      // 验证tabs数组格式是否正确
      // 修复 no-prototype-builtins：避免直接访问对象的 hasOwnProperty
      return value.every(
        tab =>
          typeof tab === 'object' &&
          Object.prototype.hasOwnProperty.call(tab, 'label') &&
          Object.prototype.hasOwnProperty.call(tab, 'value')
      )
    }
  }
})

// 定义事件
const emit = defineEmits(['update:modelValue', 'change'])

// 模板引用
const tabsNavRef = ref(null)
const tabItemRefs = ref([])
const indicatorRef = ref(null)

// 下划线指示器样式
const indicatorStyle = ref({
  width: '0px',
  transform: 'translateX(0px)',
  transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)'
})

// 处理tab点击事件
const handleTabClick = (tab, index) => {
  if (props.modelValue !== tab.value) {
    // 更新v-model值
    emit('update:modelValue', tab.value)
    // 触发change事件
    emit('change', tab.value, tab, index)
    // 更新指示器位置
    updateIndicatorPosition(index)
  }
}

// 更新下划线指示器位置
const updateIndicatorPosition = async activeIndex => {
  await nextTick()

  if (!tabItemRefs.value || !tabItemRefs.value[activeIndex]) {
    return
  }

  const activeTab = tabItemRefs.value[activeIndex]
  const tabsNav = tabsNavRef.value

  if (activeTab && tabsNav) {
    const tabRect = activeTab.getBoundingClientRect()
    const navRect = tabsNav.getBoundingClientRect()

    // 计算相对于导航容器的位置
    const offsetLeft = tabRect.left - navRect.left
    const tabWidth = tabRect.width

    // 更新指示器样式
    indicatorStyle.value = {
      width: `${tabWidth}px`,
      transform: `translateX(${offsetLeft}px)`,
      transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)'
    }
  }
}

// 初始化指示器位置
const initIndicatorPosition = () => {
  if (!props.tabs.length || !props.modelValue) return

  const activeIndex = props.tabs.findIndex(tab => tab.value === props.modelValue)
  if (activeIndex !== -1) {
    // 首次加载时不需要动画
    indicatorStyle.value.transition = 'none'
    updateIndicatorPosition(activeIndex)

    // 下一帧恢复动画
    nextTick(() => {
      indicatorStyle.value.transition = 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)'
    })
  }
}

// 监听modelValue变化
watch(
  () => props.modelValue,
  newValue => {
    const activeIndex = props.tabs.findIndex(tab => tab.value === newValue)
    if (activeIndex !== -1) {
      updateIndicatorPosition(activeIndex)
    }
  }
)

// 监听tabs数组变化
watch(
  () => props.tabs,
  () => {
    nextTick(() => {
      initIndicatorPosition()
    })
  },
  { deep: true }
)

// 组件挂载后初始化
onMounted(() => {
  nextTick(() => {
    initIndicatorPosition()
  })
})

// 监听窗口大小变化，重新计算指示器位置
const handleResize = () => {
  const activeIndex = props.tabs.findIndex(tab => tab.value === props.modelValue)
  if (activeIndex !== -1) {
    updateIndicatorPosition(activeIndex)
  }
}

onMounted(() => {
  window.addEventListener('resize', handleResize)
})

// 组件卸载时清理事件监听
import { onUnmounted } from 'vue'
onUnmounted(() => {
  window.removeEventListener('resize', handleResize)
})
</script>

<style scoped>
.custom-tabs {
  width: 100%;
}

.tabs-nav {
  position: relative;
  display: flex;
  align-items: center;
  background: #ffffff;
  overflow-x: auto;
  scrollbar-width: none; /* Firefox */
  -ms-overflow-style: none; /* IE 10+ */
}

.tabs-nav::-webkit-scrollbar {
  display: none; /* Chrome Safari */
}

.tab-item {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 6px 16px;
  font-size: 14px;
  font-weight: 500;
  color: #606266;
  cursor: pointer;
  transition: all 0.3s ease;
  white-space: nowrap;
  user-select: none;
  min-width: 80px;
}

.tab-item:hover {
  color: #409eff;
  background-color: #f5f7fa;
}

.tab-item.active {
  color: #409eff;
  font-weight: 600;
}

.tab-indicator {
  position: absolute;
  bottom: 0;
  left: 0;
  height: 2px;
  background: linear-gradient(90deg, #409eff 0%, #66b1ff 100%);
  border-radius: 2px 2px 0 0;
  z-index: 1;
}

/* 响应式设计 */
@media (max-width: 768px) {
  .tab-item {
    padding: 10px 16px;
    font-size: 13px;
    min-width: 60px;
  }
}

@media (max-width: 480px) {
  .tab-item {
    padding: 8px 12px;
    font-size: 12px;
    min-width: 50px;
  }

  .tab-indicator {
    height: 2px;
  }
}

/* 平板设备适配 */
@media (min-width: 769px) and (max-width: 1024px) {
  .tab-item {
    padding: 7px 18px;
    font-size: 15px;
  }
}

/* 大屏设备适配 */
@media (min-width: 1200px) {
  .tab-item {
    padding: 8px 22px;
    font-size: 16px;
  }

  .tab-indicator {
    height: 2px;
  }
}

/* 深色模式支持 */
@media (prefers-color-scheme: dark) {
  .tabs-nav {
    background: #2c2c2c;
    border-bottom-color: #4c4d4f;
  }

  .tab-item {
    color: #a8abb2;
  }

  .tab-item:hover {
    color: #409eff;
    background-color: #3a3a3a;
  }

  .tab-item.active {
    color: #409eff;
  }
}

/* 高对比度模式支持 */
@media (prefers-contrast: high) {
  .tab-item {
    border: 1px solid transparent;
  }

  .tab-item:hover,
  .tab-item.active {
    border-color: #409eff;
  }

  .tab-indicator {
    height: 4px;
  }
}

/* 减少动画模式支持 */
@media (prefers-reduced-motion: reduce) {
  .tab-item,
  .tab-indicator {
    transition: none;
  }
}
</style>
